/*
 * HeadFinderRule.java
 *
 * Created on August 1, 2006, 1:06 PM
 *
 */

package galronnlp.perceptron;

import galronnlp.util.Symbol;
import galronnlp.util.SymbolGenerator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * An implementation of a head finder rule as described in Appendix A in Michael
 * Collins' thesis.
 *
 * @author Daniel A. Galron
 */
public class HeadFinderRule extends LinkedList<HashSet<Symbol>> {

    /**
     * Direction over children indicating that the head finder rules should be fired left-to-right
     */
    public static int LR = 0;
    /**
     * Direction over children indicating that the head finder rules should be fired right-to-left
     */
    public static int RL = 1;

    // Default direction is left-to-right
    protected int direction = LR;

    SymbolGenerator sg;

    /**
     * Creates a new instance of HeadFinderRule
     *
     * @param dir The direction in which the head should be searched for
     * @param list A linked list where each entry in the list contains a string of space delimited 
     * symbols of equivalent priority. The list should be in order of greatest to least priority.
     * @param gen The symbol generator to use
     */
    public HeadFinderRule(int dir, LinkedList<String> list, SymbolGenerator gen) {
	this.sg = gen;
        this.direction = dir;
        // Iterate through all the strings in list
        for(Iterator<String> it = list.iterator(); it.hasNext();) {
            // Get an array of Strings where each array entry is a symbol in the
            // priority class of symbols.
            String[] next = it.next().split(" ");
            // Create a hashset of symbols, and create a symbol from each entry in
            // next and add it to the hash set.
            HashSet<Symbol> n = new HashSet<Symbol>();
            for(int i = 0; i < next.length; i++) {
                n.add(gen.create(next[i]));
            }
            // Add the hashset to the rule.
            this.addLast(n);
        }
    }

    /**
     * Fire the head finder rule, as described in Collins' thesis.
     *
     * @param n the Node at which to fire the head finder rule.
     */
    public Node fire(Node n) {
        // Iterate through all priority classes in the rule. Note that the list
        // is from greatest to least priority
        for(Iterator<HashSet<Symbol>> it = this.iterator(); it.hasNext();) {
            // Get the next class of the rule
            HashSet nextH = it.next();
            // If the rule specifies to iterate through the children of the node
            // in a ldeft-to-right manner,
            if(this.direction == LR) {
                for(Iterator<Node> ic = n.getChildren().iterator(); ic.hasNext();) {
                    // Get the candidate
                    Node candidate = ic.next();
                    // if the class considered contains the label
                    if(nextH.contains(candidate.label())) {
                        //!!!
                        // This next part is needed because of the special way
                        // that coordinating conjunctions are handled.
                        //!!!
                        // Get the index of the candidate. 
			int h = n.getChildren().indexOf(candidate);
                        // See Collins' thesis for explaination of this rule.
			if(h < (n.getChildren().size() - 2) - 1 && n.getChildren().get(h + 1).label().equals(sg.create("CC")))
                            return candidate.lexicalHead();
                        else if(h > 2 && n.getChildren().get(h - 1).label().equals(sg.create("CC")))
                            return n.getChildren().get(h - 2).lexicalHead();
                        else
                            return candidate.lexicalHead();
                    }
                }
            }
            // Otherwise, if the rule specifies to iterate through the children 
            // of the node in a right-to-left manner,
            else {
                for(int i = n.getChildren().size() - 1; i >= 0; i--) {
                    // Get the candidate
                    Node candidate = n.getChildren().get(i);
                    // if the class considered contains the label
                    if(nextH.contains(candidate.label())) {
			int h = n.getChildren().indexOf(candidate);
			if(h < (n.getChildren().size() - 2) - 1 && n.getChildren().get(h + 1).label().equals(sg.create("CC")))
                            return candidate.lexicalHead();
                        else if(h > 2 && n.getChildren().get(h - 1).label().equals(sg.create("CC")))
                            return n.getChildren().get(h - 2).lexicalHead();
                        else
                            return candidate.lexicalHead();
                    }
                }
            }
        }
        return n.getChildren().getLast().lexicalHead();
    }



}
